/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 * 
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 * 
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * The Original Code is "SSH Console" code.
 * 
 * The Initial Developer of the Original Code is Todd Whiteman.
 * Portions created by the Initial Developer are Copyright (C) 2008
 * the Initial Developer. All Rights Reserved.
 * 
 * Contributor(s):
 *   Todd Whiteman <twhitema@gmail.com> (original author)
 * 
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 * 
 * ***** END LICENSE BLOCK ***** */

// Global window variables.
var histList = [""];
var histPos = 0;
var _state = "disconnected"; // Either connected or disconnected.
var _ssh_channel = null; // xpcom python ssh channel
var _term_box_element = null;
var _term = null;
var _screen = null;
var _scrollbox = null;
var question;
var _in;
var _out;
var _scroll_on_keypress = false;
var _scroll_on_output = false;
var _had_keypress = false;
var _window_scroll_size = 0;

var pref_observer = {
    observe: function(subject, topic, data) {
        if (topic != "nsPref:changed") {
            return;
        }

        var prefsMain = Components.classes["@mozilla.org/preferences-service;1"]
                        .getService(Components.interfaces.nsIPrefService);
        var prefs = prefsMain.getBranch("extensions.sshconsole.");
        prefs.QueryInterface(Components.interfaces.nsIPrefBranch2);

        switch(data) {
            case "color.fg":
              _term.set_fg_color(prefs.getIntPref("color.fg"));
              break;
            case "color.bg":
              _term.set_bg_color(prefs.getIntPref("color.bg"));
              break;
            case "scroll.lines":
              _term.set_max_scroll_lines(prefs.getIntPref("scroll.lines"));
              break;
            case "scroll.lines":
              _term.set_max_scroll_lines(prefs.getIntPref("scroll.lines"));
              break;
            case "scroll.on_keypress":
                _scroll_on_keypress = prefs.getBoolPref("scroll.on_keypress");
              break;
            case "scroll.on_output":
                _scroll_on_output = prefs.getBoolPref("scroll.on_output");
              break;
        }
    }
}

function onLoad() {
    try {
        var prefsMain = Components.classes["@mozilla.org/preferences-service;1"]
                        .getService(Components.interfaces.nsIPrefService);
        var prefs = prefsMain.getBranch("extensions.sshconsole.");
        prefs.QueryInterface(Components.interfaces.nsIPrefBranch2);
        prefs.addObserver("", pref_observer, false);
        var max_lines = prefs.getIntPref("scroll.lines");
        _scroll_on_output = prefs.getBoolPref("scroll.on_output");
        _scroll_on_keypress = prefs.getBoolPref("scroll.on_keypress");
        var fg = prefs.getIntPref("color.fg");
        var bg = prefs.getIntPref("color.bg");

        _term_box_element = document.getElementById("term_box");
        _screen = document.getElementById("term");
        //_scrollbox = _term_box_element.boxObject.QueryInterface(Components.interfaces.nsIScrollBoxObject);
        _term = new VT100(80, 24, "term", max_lines, fg, bg);
        //_term.debug_ = 1;
        _term.curs_set(true, true, _term_box_element);
        _term.noecho();

        // Ensure the terminal maintains the same height, so scrolling triggers.
        _window_scroll_size = _term_box_element.scrollHeight;
        _term_box_element.style.height = _window_scroll_size + "px";

        // Replace the go_getch_ function with our own, this is called
        // for every keypress that is passed through the terminal to the
        // remote server. The character is already converted into the
        // required VT100 character sequence(s).
        VT100.go_getch_ = function() {
            var vt = VT100.the_vt_;
            if (vt === undefined) {
                    return;
            }
            var ch = vt.key_buf_.shift();
            //dump("go_getch_:: ch: '" + ch + "'\n");
            if (ch === undefined) {
                    return;
            }
            if (vt.echo_ && ch.length == 1) {
                    vt.addch(ch);
            }
            if (_ssh_channel) {
                _ssh_channel.sendStdin(ch);
            }
            _had_keypress = true;
        }

        var serverTextbox = document.getElementById("sshconsole_server_textbox");
        var connectionText;
        if (window.arguments && 'connectionText' in window.arguments[0]) {
            connectionText = window.arguments[0].connectionText;
        } else {
            // Get the "extensions.sshconsole." pref branch.
            var prefs = Components.classes["@mozilla.org/preferences-service;1"].
                                getService(Components.interfaces.nsIPrefService);
            prefs = prefs.getBranch("extensions.sshconsole.");
            connectionText = prefs.getCharPref("lastConnection");
        }

        serverTextbox.value = connectionText;
        serverTextbox.focus();

        if (window.arguments && 'connectionURI' in window.arguments[0]) {
            sshconsole_connect_uri(window.arguments[0].connectionURI);
        }

    } catch(ex) {
        alert("onLoad:: error: " + ex);
    }
}

function onUnload() {
    try {
        set_state("disconnected");
        // Get the "extensions.sshconsole." pref branch.
        var prefsMain = Components.classes["@mozilla.org/preferences-service;1"]
                        .getService(Components.interfaces.nsIPrefService);
        var prefs = prefsMain.getBranch("extensions.sshconsole.");
        prefs.QueryInterface(Components.interfaces.nsIPrefBranch2);
        prefs.removeObserver("", pref_observer);
        prefs.setCharPref("lastConnection",
                          document.getElementById("sshconsole_server_textbox").value);
    } catch(ex) {
        alert("onUnload:: error: " + ex);
    }
}

/**
 * Inspect code is from Prototype or jquery...
 */
var specialChar = {
    '\b': '\\b',
    '\t': '\\t',
    '\n': '\\n',
    '\f': '\\f',
    '\r': '\\r',
    '\\': '\\\\'
};
function toPaddedString(s, length, radix) {
    var string = s.toString(radix || 10);
    while (string.length < length) {
        string = '0' + string;
    }
    return string;
}
function inspect(s, useDoubleQuotes) {
  var escapedString = s.replace(/[\x00-\x1f\\]/g, function(match) {
    var character = specialChar[match[0]];
    return character ? character : '\\x' + toPaddedString(match[0].charCodeAt(), 2, 16);
  });
  if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
  return "'" + escapedString.replace(/'/g, '\\\'') + "'";
}

var sshChannelCallback = {
    onStartRequest: function(channel, ctxt) {
        //dump("onStartRequest\n");
    },
    onDataAvailable: function(channel, ctxt, input, sourceOffset, count) {
        try {
            /* Convert the input into unicode from UTF-8 encoding. */
            const replacementChar = Components.interfaces.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER;
            var is = Components.classes["@mozilla.org/intl/converter-input-stream;1"]
                        .createInstance(Components.interfaces.nsIConverterInputStream);
            is.init(input, "UTF-8", 4096, replacementChar);
            var data = "";
            var str = {};
            var width_obj = {};
            var height_obj = {};
            var scroll_size;
            var scroll_size_delta;
            var scroll_pos;
            var scroll_pos_delta;
            while (is.readString(4096, str) != 0) {
                data += str.value;
            }

            //dump("data: " + inspect(data) + "\n");

            var scrollWasAtBottom = (_term_box_element.scrollHeight - _term_box_element.scrollTop) <= _window_scroll_size;
            /* Write the data to the terminal. */
            _term.write(data);
            var scrollNowAtBottom = (_term_box_element.scrollHeight - _term_box_element.scrollTop) <= _window_scroll_size;
            /* scroll the terminal to the bottom, so the latest output can be
               seen. */
            if (!scrollNowAtBottom &&
                (scrollWasAtBottom
                 || _scroll_on_output
                 || (_scroll_on_keypress && _had_keypress)
                 || (scroll_size_delta >= 0 && scroll_pos_delta >= 0)
                 || (scroll_size_delta < 0))
                ) {
                _term_box_element.scrollTop = _term_box_element.scrollHeight - _window_scroll_size;
            }
            _had_keypress = false;
        } catch(ex) {
            dump("onDataAvailable exception: " + ex + "\n");
        }
    },
    onStopRequest: function(channel, ctxt, status) {
        //dump("onStopRequest\n");
    }
};

function set_state(new_state) {
    _state = new_state;
    var btn = document.getElementById("sshconsole_connect_button");
    if (new_state == "disconnected") {
        if (_ssh_channel) {
            btn.setAttribute("label", "Connect");
            _ssh_channel.cancel(Components.results.NS_OK);
            _ssh_channel = null;
        }
    } else {
        btn.setAttribute("label", "Disconnect");
        btn.blur();
        focusTerminal();
    }
}

function sshconsole_connect_uri(uri_spec) {
    if (_state != "disconnected") {
        set_state("disconnected");
    }
    if (uri_spec.substring(0, 6) != "ssh://") {
        uri_spec = "ssh://" + uri_spec + "/";
    }
    _term.clear_all();
    const ioSvc = Components.classes["@mozilla.org/network/io-service;1"].
                        getService(Components.interfaces.nsIIOService);
    var nsUri = ioSvc.newURI(uri_spec, null, null);
    var channel = ioSvc.newChannelFromURI(nsUri);
    _ssh_channel = channel.QueryInterface(Components.interfaces.pyISSHInteraction);
    _ssh_channel.asyncInteract(sshChannelCallback, "vt100", null);
    set_state("connected");
}

function sshconsole_connect() {
    try {
        if (_state == "disconnected") {
            var uri_spec = document.getElementById("sshconsole_server_textbox").value;
            if (!uri_spec) {
                alert("You'll first need to enter the server name.");
                return;
            }
            sshconsole_connect_uri(uri_spec);
        } else {
            set_state("disconnected");
        }
    } catch(ex) {
        alert(ex);
    }
}

function focusTerminal() {
    _term_box_element.focus();
}

function sshconsole_quit() {
    window.close();
}

function sshconsole_copy() {
    //dump("sshconsole_copy\n");
    var sel = window.getSelection();
    var clipboardHelper = Components.classes["@mozilla.org/widget/clipboardhelper;1"].
                                getService(Components.interfaces.nsIClipboardHelper);
    clipboardHelper.copyString(sel.toString());
}

function sshconsole_paste() {
    //dump("sshconsole_paste\n");
    if (!_ssh_channel) {
        return false;
    }

    var clipSvc = Components.classes["@mozilla.org/widget/clipboard;1"]
                    .getService(Components.interfaces.nsIClipboard);
    var trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);  
    trans.addDataFlavor("text/unicode");
    clipSvc.getData(trans, clipSvc.kGlobalClipboard);
    var str       = new Object();  
    var strLength = new Object();  
    trans.getTransferData("text/unicode", str, strLength);
    if (str) {
        str = str.value.QueryInterface(Components.interfaces.nsISupportsString);
        if (str) {
            var pastetext = str.data.substring(0, strLength.value / 2);
            _ssh_channel.sendStdin(pastetext);
            return true;
        }
    }
    return false;
}

function sshconsole_settings() {
    window.openDialog("chrome://sshconsole/content/sshconsole-prefs.xul",
                      "_blank",
                      "modal,resizable=yes,toolbar");
}
